﻿/*
* 20110420 syncSprite, copy form com.cf.core.control.SyncSprite;
* we can sync MovieClip & Sprite by same method.
* 20101024
* bug fix, callback 避免使用後, 其他 playing 方式沿用舊 callback function 造成錯誤觸發錯覺
* 
* 控制 MovieClip 上之 Label 播放方式
* playOne(_fr:String) // 播放特定影格一次, null = 全片播放一次
* playAll()
* playLoop('影格':String, 重播:Boolean, 重播次數:int);
* 支援 callback, 於影片播放完結後執行預定 function
* findLabel("frame name"):Boolean 搜尋影片中是否擁有該影格.
* By Canis. 20091007
* 
* 
*/
package com.cf.core.model{
	import flash.display.MovieClip;
	import com.cf.core.model.AutoDestoryClip;
	import flash.display.SimpleButton; // fix CS3 bug
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import com.cf.core.Utils.tools;
	import com.cf.core.events.UIEvent;
	import flash.display.FrameLabel;
	import flash.utils.setTimeout;
	public class SmartClipBase extends AutoDestoryClip{
		private var __PlayingFrame:*; // record current Frame Label.
		public function get playingFrame():*{return __PlayingFrame}
		private var __loop:Boolean = false; // current playback loop or not
		private var __times:int;	// how many times it needed to loop
		private var __currentLap:int; // counter how many time it loop.
		private var __startPIN:int; // current label's start frame no
		private var __endPIN:int; // current label's end frame no
		private var __callbackFn:Function; // after Finish do some function.
		public function set callback(_fn:Function):void{__callbackFn=_fn}
		public function get callback():Function{return __callbackFn}
		private var __rewind:Boolean = false; // make Movie "play to end" / "play to start"
		public function set rewind(_tf:Boolean):void{__rewind=_tf}
		public function get rewind():Boolean{return __rewind}
		private var __alert:Boolean = false;
		public function set alert(_tf:Boolean):void{__alert=_tf}
		public function get alert():Boolean{return __alert}
		private var __internal:Boolean=false;
		protected function set internal(_tf:Boolean):void{__internal=_tf}
		protected function get internal():Boolean{return __internal}
		// action label
		private var __action:String // recording what action it doing
		public function set action(_s:String):void{__action=_s}
		public function get action():String{return __action}
		private var __pause:Boolean;
		private var __pauseb4:String;
		static const STOP:String = "SmartClipSTOP";
		static const PAUSE:String = "SmartClipPause";
		static const PLAYONE:String = "SmartClipPlayONE";
		static const PLAYALL:String = "SmartClipPlayAll";
		static const PLAYLOOP:String = "SmartClipPlayLoop";
		public function get frame():String{
			return this.currentLabel;
		}//fn
		
		public function SmartClipBase(){
			stop();
			__pause=false;
			action=STOP;
			callback=new Function();
			addEventListener(UIEvent.LOADED, handleAfterEffect);
		}//fn
		override public function init():void{
			G_width=false;
			G_height=false;
			G_x=false;
			G_y=false;
			G_scaleX=false;
			G_scaleY=false;
			G_event=new Event(Event.RESIZE);
		}//fn
		public function pause():void{
			if(!__pause && action!=STOP){
				__pauseb4=action;
				__pause=true;
			}//if
		}//fn
		public function resume():void{
			if(__pause && action!=STOP){
				action=__pauseb4;
				__pause=false;
				// handleClipLoop will keep going.
			}//if
		}//fn
		// interal
		// Find out whole MovieClip's Label ,report in Array
		protected function labelLength(_fr:String):Object{
			var _rtn:Object = new Object();
			_rtn.exist	= false;
			_rtn.name	= _fr;
			_rtn.start	= 0;
			_rtn.end	= 0;
			var _labels:Array = this.currentLabels;
			for (var i:uint = 0; i<_labels.length; i++) {
				var _label:FrameLabel = _labels[i];
				if(alert) trace("frame " + _label.frame + ": " + _label.name);
				if(_rtn.name==_label.name){
					// trace("Found!!");
					_rtn.exist=true;
					_rtn.start=_label.frame;
					if(_labels[i+1]!=undefined){
						// self end = before next label clip's frame start-1
						_rtn.end = _labels[i+1].frame -1;
					}else{
						// This is the last Label we have, set to movie's end.
						_rtn.end = this.totalFrames;
					}//if
					break;
				}//if
			}//for
			//trace("["+_rtn.exist +"] name:"+_rtn.name+" start:"+_rtn.start+" end:"+_rtn.end);
			return _rtn;  // format output [exist:T/F, name=_fr, start:##, end:##]
		}//fn
		
		public function findLabel(_fr:String):Boolean{
			return Boolean(labelLength(_fr).exist);
		}//fn
		
		/*
		* 
		* Sub Function extends for "SmartClipBase" .
		* public functon
		*/
		//
		// 成條片 play 1 次, 輸入值則只 Play framelabel 一次.
		//
		public function playOne(_fr:String=''):void{
			if(_fr==""){
				if(action!=STOP) handleStopLoop(false); // clear listener before workon this.
				action=PLAYONE;
				__startPIN = 1;
				__endPIN = this.totalFrames;
				__loop=false;
				gotoAndLoop(); // fix PIN head when rewind=true;
				addEventListener(Event.ENTER_FRAME, handleClipLoop);
			}else{
				playLoop(_fr,false,0);
			}//if
		}//fn

		//
		// Loop 死條片
		//
		public function playAll():void{
			if(action!=STOP) handleStopLoop(false); // clear listener before workon this.
			action=PLAYALL;
			__PlayingFrame="";
			__startPIN=1;
			__endPIN=this.totalFrames;
			__loop=true;
			gotoAndLoop(); // fix PIN head when rewind=true;
			addEventListener(Event.ENTER_FRAME, handleClipLoop);
		}//fn
		//
		// 停返個 Loop
		//
		public function stopAll(_callbackAllow:Boolean=false):void{
			// it wouldn't stop before user call it.
			// true = active callback function.
			// false = play to this clip end, and do nothing.
			if(action!=STOP) handleStopLoop(_callbackAllow);
		}//fn
		
		public function playLoop(_fr:String='',_loop:Boolean=true,_cnt:int=0):void{
			if(!internal){ // stop run while it call by it's extends function.
				if(action!=STOP) handleStopLoop(false); // clear listener before workon this.
				action=PLAYLOOP;
			}//if
			// only replay play the label frame
			__PlayingFrame = _fr; // flag
			__loop = _loop; // loop flag
			__times = (_cnt<=0)?0:_cnt; // loop times
			__currentLap = 1; // init current lap;
			var _tmpobj:Object = labelLength(_fr);
			if(_tmpobj.exist){
				__startPIN = _tmpobj.start; // label's start, not MovieClip's start
				__endPIN = _tmpobj.end; // label's end, not MovieClip's end
				if(!__rewind){
					gotoAndStop(__startPIN);
				}else{
					gotoAndStop(__endPIN);
				}//if
				addEventListener(Event.ENTER_FRAME, handleClipLoop);
			}else{
				trace("SmartClip Error : no such frame label'"+_fr+"' form "+ this);
				stopAll();
			}//if
		}//fn
		
		///////////////////////////////////////////////////////////////////
		// engine of playback control
		// private function
		protected function handleClipLoop(e:Event):void{
			// while in label's range
			if(!__pause){
				if(__startPIN == __endPIN){
					// don't waste CPU
					handleStopLoop();
				}else if(this.currentFrame >= __startPIN && this.currentFrame <= __endPIN){
					// prevent goto other frame.
					if(__rewind){
						if(this.currentFrame!=__startPIN){ prevFrame(); }
						else handleClipNextLoop();
					}else{
						if(this.currentFrame!=__endPIN){ nextFrame(); }
						else handleClipNextLoop();
					}//if
				}else{
					trace("handleClipLoop : out of range");
				}//if
			}else{
				trace("handleClipLoop : Pause");
			}//if
		}//fn
		protected function handleClipNextLoop():void{
			// out of range
			if(__loop){
				if(__times==0){ // zero = unlimit
					gotoAndLoop();
					if(alert) trace(this.currentLabel +" repeat");
				}else if(__currentLap<__times){
					__currentLap++;
					gotoAndLoop();
					if(alert) trace(this.currentLabel +" mutli times :- "+ __currentLap);
				}else if(__currentLap>=__times){
					// reach loop limit
					handleStopLoop();
					if(alert) trace(this.currentLabel +" reach loop limit ");
				}//if
			}else{
				// no loop
				handleStopLoop();
				if(alert) trace(this.currentLabel +" only play one");
			}//if
		}//fn
		protected function gotoAndLoop():void{
			if(!__rewind){
				gotoAndStop(__startPIN);
			}else{
				gotoAndStop(__endPIN);
			}//if
		}//fn
		protected function handleStopLoop(_alert:Boolean=true):void{
			if(_alert && !internal){
				// dispatch, clear and out.
				dispatchEvent(new UIEvent(UIEvent.LOADED, {action:__action}));
			}//if
			if(alert) trace("handleStopLoop : "+ this +" "+ action +" "+ _alert);
			removeEventListener(Event.ENTER_FRAME, handleClipLoop);
			action=STOP;
			dispatchEvent(new UIEvent(UIEvent.EVENT, {action:__action}));
			stop();
		}//fn
		protected function handleAfterEffect(e:UIEvent):void{
			if(alert) trace("handleAfterEffect "+ this +" callback("+ callback +")");
			// 不能直接執行
			// 規避徊圈錯誤, 觸發&完結所有 event 程序後額外執行.
			setTimeout(callback, 0);
			callback=new Function();	// Clear up after job done.
		}//fn
		
		//
		// SyncSprite object.
		//
		private var G_objA:DisplayObjectContainer;	
		private var G_event:Event;
		private var G_width,G_height,G_x,G_y,G_scaleX,G_scaleY,G_alpha,G_depth,G_cover:Boolean;
		public function syncFrom(_oa:DisplayObjectContainer):void{
			G_objA=_oa;
			G_objA.addEventListener(Event.RESIZE, handleSync);	// target DisplayObject must contain SyncSprite
		}//fn
		private function handleSync(e:Event):void{
			if(this.syncWidth)		this.width=e.target.width;
			if(this.syncHeight)		this.height=e.target.height;
			if(this.syncX)			this.x=e.target.x;
			if(this.syncY)			this.y=e.target.y;
			if(this.syncAlpha)		this.alpha=e.target.alpha;
			if(this.syncScaleX)		this.scaleX=e.target.scaleX;
			if(this.syncScaleY)		this.scaleY=e.target.scaleY;
			if(G_depth){
				if(G_cover)			e.target.parent.setChildIndex(this, e.target.getChildIndex+1);
				else				e.target.parent.setChildIndex(this, e.target.getChildIndex-1);
			}
		}//fn
		
		public function get target():DisplayObjectContainer{return G_objA}
		public override function set height(_n:Number):void{super.height=_n;	dispatchEvent(G_event);}
		public override function set width(_n:Number):void{super.width=_n;		dispatchEvent(G_event);}
		public override function set x(_n:Number):void{super.x=_n;				dispatchEvent(G_event);}
		public override function set y(_n:Number):void{super.y=_n;				dispatchEvent(G_event);}
		public override function set alpha(_n:Number):void{super.alpha=_n;		dispatchEvent(G_event);}
		public override function set scaleX(_n:Number):void{super.scaleX=_n;	dispatchEvent(G_event);}
		public override function set scaleY(_n:Number):void{super.scaleY=_n;	dispatchEvent(G_event);}
		public override function setChildIndex(_c:DisplayObject, _n:int):void{
			super.setChildIndex(_c,_n);
			dispatchEvent(G_event);
		}//fn
		
		public function set syncWidth(_tf:Boolean):void{G_width=_tf}
		public function get syncWidth():Boolean{return G_width}
		public function set syncHeight(_tf:Boolean):void{G_height=_tf}
		public function get syncHeight():Boolean{return G_height}
		public function set syncX(_tf:Boolean):void{G_x=_tf}
		public function get syncX():Boolean{return G_x}
		public function set syncY(_tf:Boolean):void{G_y=_tf}
		public function get syncY():Boolean{return G_y}
		public function set syncAlpha(_tf:Boolean):void{G_alpha=_tf}
		public function get syncAlpha():Boolean{return G_alpha}
		public function set syncScaleX(_tf:Boolean):void{G_scaleX=_tf}
		public function get syncScaleX():Boolean{return G_scaleX}
		public function set syncScaleY(_tf:Boolean):void{G_scaleY=_tf}
		public function get syncScaleY():Boolean{return G_scaleY}
		
		//
		// Depth
		//
		public function set syncDepth(_tf:Boolean):void{
			this.syncDepthCover=_tf;
		}
		public function set syncDepthCover(_tf:Boolean):void{
			G_depth=_tf;
			G_cover=true;
		}
		public function set syncDepthRear(_tf:Boolean):void{
			G_depth=_tf;
			G_cover=false;
		}
	}//class
}//package